package cn.lingyangwl.agile.system.service.impl;

import cn.lingyangwl.agile.auth.AuthApi;
import cn.lingyangwl.agile.model.BasePlusPO;
import cn.lingyangwl.agile.model.enums.RoleEnum;
import cn.lingyangwl.agile.system.assembly.SysRoleAssembly;
import cn.lingyangwl.agile.system.manager.SysRoleDeptRelManager;
import cn.lingyangwl.agile.system.manager.SysRoleManager;
import cn.lingyangwl.agile.system.manager.SysRoleMenuManager;
import cn.lingyangwl.agile.system.manager.SysUserManager;
import cn.lingyangwl.agile.system.mapper.SysRoleDeptRelMapper;
import cn.lingyangwl.agile.system.mapper.SysRoleMapper;
import cn.lingyangwl.agile.system.mapper.SysRoleMenuRelMapper;
import cn.lingyangwl.agile.system.mapper.SysUserRoleMapper;
import cn.lingyangwl.agile.system.model.entity.SysRole;
import cn.lingyangwl.agile.system.model.entity.SysRoleDeptRel;
import cn.lingyangwl.agile.system.model.entity.SysRoleMenu;
import cn.lingyangwl.agile.system.model.entity.SysUserRole;
import cn.lingyangwl.agile.system.model.rqrs.role.*;
import cn.lingyangwl.agile.system.service.SysRoleService;
import cn.lingyangwl.framework.mybatis.plus.utils.PageUtil;
import cn.lingyangwl.framework.tool.core.CollectionUtils;
import cn.lingyangwl.framework.tool.core.StringUtils;
import cn.lingyangwl.framework.tool.core.exception.Assert;
import cn.lingyangwl.framework.tool.core.exception.BizException;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.util.*;
import java.util.stream.Collectors;

import static cn.lingyangwl.agile.system.constants.SysErrorEnum.*;


/**
 * @author shenguangyang
 */
@Service
public class SysRoleServiceImpl implements SysRoleService {
    private static final Logger log = LoggerFactory.getLogger(SysRoleServiceImpl.class);

    @Resource
    private SysRoleMapper roleMapper;
    @Resource
    private SysRoleManager roleManager;
    
    @Resource
    private SysRoleMenuRelMapper roleMenuRelMapper;

    @Resource
    private SysRoleMenuManager roleMenuManager;
    @Resource
    private SysUserRoleMapper userRoleMapper;

    @Resource
    private SysRoleDeptRelMapper roleDeptRelMapper;
    @Resource
    private SysRoleDeptRelManager roleDeptManager;
    @Resource
    private SysRoleAssembly roleAssembly;
    @Resource
    private AuthApi authApi;
    @Resource
    private SysUserManager userManager;

    /**
     * 根据条件分页查询角色数据

     * @return 角色数据集合信息
     */
    @Override
    public IPage<RoleResp> pageRole(RolePageReq req) {
        LambdaQueryWrapper<SysRole> lqw = SysRole.lqw()
                .like(StringUtils.isNotEmpty(req.getRoleName()), SysRole::getRoleName, req.getRoleName())
                .eq(Objects.nonNull(req.getSysBuiltIn()), SysRole::getSysBuiltIn, req.getSysBuiltIn())
                .orderByAsc(SysRole::getRoleSort)
                .orderByDesc(SysRole::getUpdatedAt);
        IPage<SysRole> page = roleMapper.selectPage(PageUtil.to(req), lqw);

        return PageUtil.to(page, (e) -> roleAssembly.toRoleResp(e));
    }

    @Override
    public List<RoleResp> listRole(RoleListReq req) {
        LambdaQueryWrapper<SysRole> lqw = SysRole.lqw()
                .like(StringUtils.isNotEmpty(req.getRoleName()), SysRole::getRoleName, req.getRoleName())
                .eq(Objects.nonNull(req.getSysBuiltIn()), SysRole::getSysBuiltIn, req.getSysBuiltIn())
                .orderByDesc(BasePlusPO::getUpdatedAt);
        return roleMapper.selectList(lqw).stream().map(roleAssembly::toRoleResp).collect(Collectors.toList());
    }

    /**
     * 根据用户ID查询权限
     *
     * @param userId 用户ID
     * @return 权限列表
     */
    @Override
    public Set<String> listRolePermission(Long userId) {
        List<SysRole> perms = roleMapper.selectRolePermissionByUserId(userId);
        Set<String> permsSet = new HashSet<>();
        perms.stream().filter(Objects::nonNull)
                .forEach(item -> permsSet.addAll(Arrays.asList(item.getRoleCode().trim().split(","))));
        return permsSet;
    }

    @Override
    public List<SysRole> listRoleByUserId(Long userId) {
        return roleMapper.selectRolePermissionByUserId(userId);
    }

    /**
     * 查询所有角色
     *
     * @return 角色列表
     */
    @Override
    public List<RoleResp> listAllRole() {
        List<RoleResp> roleList = this.listRole(new RoleListReq());
        // 处理租户拥有者, 拥有者角色, 在一个租户中只能有一个用户拥有
        return roleList.stream().filter(e -> !RoleEnum.OWNER.getCode().equals(e.getRoleCode()))
                .collect(Collectors.toList());
    }

    /**
     * 根据用户ID获取角色选择框列表
     *
     * @param userId 用户ID
     * @return 选中角色ID列表
     */
    @Override
    public List<Long> listRoleIdsByUserId(Long userId) {
        return userRoleMapper.selectList(SysUserRole.lqw().eq(SysUserRole::getUserId, userId)).stream()
                .map(SysUserRole::getRoleId).distinct().collect(Collectors.toList());
    }

    /**
     * 通过角色ID查询角色
     *
     * @param roleId 角色ID
     * @return 角色对象信息
     */
    @Override
    public SysRole getRoleById(Long roleId) {
        return roleMapper.selectRoleById(roleId);
    }

    /**
     * 校验角色是否允许操作
     *
     * @param role 角色信息
     */
    @Override
    public void checkRoleAllowed(SysRole role) {

    }

    /**
     * 通过角色ID查询角色使用数量
     *
     * @param roleId 角色ID
     * @return 结果
     */
    @Override
    public int countUserRoleByRoleId(Long roleId) {
        LambdaQueryWrapper<SysUserRole> wrapper = Wrappers.<SysUserRole>lambdaQuery()
                .eq(SysUserRole::getRoleId, roleId);
        return Math.toIntExact(userRoleMapper.selectCount(wrapper));
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Long saveRole(RoleSaveReq req) {
        SysRole role = roleAssembly.toRole(req);
        validateRoleDuplicate(req.getRoleName(), req.getRoleCode(), null);
        roleManager.save(role);
        return role.getId();
    }

    /**
     * 修改保存角色信息
     *
     * @param req 角色信息
     * @return 结果
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean updateRoleById(RoleUpdateReq req) {
        SysRole role = roleAssembly.toRole(req);
        validateRoleForUpdate(role.getId());
        validateRoleDuplicate(role.getRoleName(), role.getRoleCode(), role.getId());
        return roleManager.updateById(role);
    }

    void validateRoleForUpdate(Long id) {
        SysRole role = roleMapper.selectById(id);
        if (role == null) {
            throw new BizException(ROLE_NOT_EXISTS);
        }

        if (RoleEnum.isTenantOwner(role.getRoleCode()) || RoleEnum.isAdmin(role.getRoleCode())) {
            throw new BizException(ROLE_CAN_NOT_OPERATE_ADMIN);
        }
    }

    /**
     * 校验角色的唯一字段是否重复
     *
     * 1. 是否存在相同名字的角色
     * 2. 是否存在相同编码的角色
     *
     * @param name 角色名字
     * @param code 角色额编码
     * @param id 角色编号
     */
    void validateRoleDuplicate(String name, String code, Long id) {
        // 0. 管理员，不允许创建
        if (RoleEnum.isAdmin(code)) {
            throw new BizException(ROLE_ADMIN_CODE_ERROR, code);
        }
        // 1. 该 name 名字被其它角色所使用
        SysRole role = roleManager.getByName(name);
        if (role != null && !role.getId().equals(id)) {
            throw new BizException(ROLE_NAME_DUPLICATE, name);
        }

        // 2. 是否存在相同编码的角色
        if (!org.springframework.util.StringUtils.hasText(code)) {
            return;
        }

        // 该 code 编码被其它角色所使用
        role = roleManager.getByCode(code);
        if (role != null && !role.getId().equals(id)) {
            throw new BizException(ROLE_CODE_DUPLICATE, code);
        }
    }

    /**
     * 修改角色状态
     *
     * @param role 角色信息
     * @return 结果
     */
    @Override
    public int updateRoleStatus(SysRole role) {
        return roleMapper.updateById(role);
    }

    /**
     * 修改数据权限信息
     *
     * @param role 角色信息
     * @return 结果
     */
    @Override
    @Transactional
    public int authDataScope(SysRole role) {
        // 修改角色信息
        roleMapper.updateById(role);
        // 删除角色与部门关联
        roleDeptManager.deleteByRoleId(role.getId());
        // 新增角色和部门信息（数据权限）
        return insertRoleDept(role);
    }

    /**
     * 新增角色部门信息(数据权限)
     *
     * @param role 角色对象
     */
    public int insertRoleDept(SysRole role) {
        int rows = 1;
        // 新增角色与部门（数据权限）管理
        List<SysRoleDeptRel> list = new ArrayList<>();
        for (Long deptId : role.getDeptIds()) {
            SysRoleDeptRel rd = new SysRoleDeptRel();
            rd.setRoleId(role.getId());
            rd.setDeptId(deptId);
            list.add(rd);
        }
        if (list.size() > 0) {
            rows = roleDeptRelMapper.batchRoleDept(list);
        }
        return rows;
    }

    /**
     * 批量删除角色信息
     *
     * @param roleIds 需要删除的角色ID
     * @return 结果
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean deleteRoleByIds(Long[] roleIds) {
        List<Long> deleteRoleIds = new ArrayList<>(roleIds.length);
        Arrays.stream(roleIds).filter(Objects::nonNull).forEach(roleId -> {
            SysRole role = getRoleById(roleId);
            if (Objects.isNull(role)) {
                return;
            }

            RoleEnum roleEnum = RoleEnum.ofByCode(role.getRoleCode());
            if (Objects.nonNull(roleEnum)) {
                throw new BizException(ROLE_CAN_NOT_DELETE_SYSTEM);
            }

            if (Boolean.TRUE.equals(role.getSysBuiltIn())) {
                throw new BizException(ROLE_CAN_NOT_UPDATE_SYSTEM_TYPE_ROLE);
            }

            if (countUserRoleByRoleId(roleId) > 0) {
                throw new BizException(String.format("%1$s已分配,不能删除", role.getRoleName()));
            }
            deleteRoleIds.add(roleId);
        });
        if (CollectionUtils.isEmpty(deleteRoleIds)) {
            return true;
        }
        // 删除角色与菜单关联
        roleMenuManager.deleteByRoleIds(deleteRoleIds);
        // 删除角色与部门关联
        roleDeptManager.deleteByRoleIds(deleteRoleIds);
        boolean ret = roleManager.deleteByIds(deleteRoleIds);
        // 刷新绑定该角色的客户端
        authApi.refreshClientAuthorityByRoleIds(Arrays.stream(roleIds).collect(Collectors.toList()), true);
        return ret;
    }

    @Override
    public SysRoleMenu getRoleMenu(String permissionId, String roleId) {
        LambdaQueryWrapper<SysRoleMenu> query = new LambdaQueryWrapper<SysRoleMenu>()
                .eq(SysRoleMenu::getMenuId, permissionId)
                .isNotNull(SysRoleMenu::getDataRuleIds)
                .eq(SysRoleMenu::getRoleId,roleId);
        return roleMenuRelMapper.selectOne(query);
    }

    @Override
    @Transactional
    public void saveRoleMenu(RolePermissionDataRuleSaveReq req) {
        SysRoleMenu sysRoleMenu = roleAssembly.toRoleMenuRel(req);
        // 先删除后插入
        LambdaQueryWrapper<SysRoleMenu> deleteLqw = SysRoleMenu.lqw()
                .eq(SysRoleMenu::getRoleId, sysRoleMenu.getRoleId())
                .eq(SysRoleMenu::getMenuId, sysRoleMenu.getMenuId());
        roleMenuRelMapper.delete(deleteLqw);

        if (roleMenuRelMapper.insert(sysRoleMenu) <= 0) {
            throw new BizException("保存失败");
        }

        // 由于角色数据规则已经被更改, 则清空所有token, 使所有用户重新登录
        List<Long> userIds = userManager.listUserIdsByRoleIds(Collections.singletonList(req.getRoleId()));
        authApi.removeAccessTokenByUserIds(userIds);
    }

    @Override
    public void addUser(AssignRoleUserReq req) {
        List<Long> newUserIdList = req.getUserIdList();
        Long roleId = req.getRoleId();
        if (CollectionUtils.isEmpty(req.getUserIdList())) {
            log.info("userIds is empty");
            return;
        }

        // 去重, 同一个用户不能绑定两次角色
        LambdaQueryWrapper<SysUserRole> findLqw = new LambdaQueryWrapper<>();
        findLqw.eq(SysUserRole::getRoleId, req.getRoleId());
        findLqw.in(SysUserRole::getUserId, req.getUserIdList());
        List<SysUserRole> sysUserRoles = userRoleMapper.selectList(findLqw);
        Map<Long, Long> userIdMap = sysUserRoles.stream().collect(Collectors.toMap(SysUserRole::getUserId, SysUserRole::getUserId, (key1, key2) -> key2));
        newUserIdList.removeIf(userId -> Objects.nonNull(userId) && userIdMap.containsKey(userId));

        List<SysUserRole> sysUserRoleList = new ArrayList<>();
        for (Long userId : newUserIdList) {
            SysUserRole rel = new SysUserRole();
            rel.setUserId(userId);
            rel.setRoleId(roleId);
            sysUserRoleList.add(rel);
        }
        if (!sysUserRoleList.isEmpty() && userRoleMapper.batchSaveUserRole(sysUserRoleList) != sysUserRoleList.size()) {
            throw new BizException("添加用户失败");
        }
    }

    @Override
    public void deleteUser(Long userId, Long roleId) {
        Assert.notNull(userId, "请选择用户");
        Assert.notNull(roleId, "请选择角色");
        LambdaQueryWrapper<SysUserRole> deleteLqw = new LambdaQueryWrapper<>();
        deleteLqw.eq(SysUserRole::getRoleId, roleId);
        deleteLqw.eq(SysUserRole::getUserId, userId);
        if (userRoleMapper.delete(deleteLqw) == 0) {
            throw new BizException("删除失败, 请联系管理员");
        }
    }

    @Override
    public void deleteBatchUser(List<Long> userIds, Long roleId) {
        Assert.notNull(userIds, "请选择用户");
        Assert.notNull(roleId, "请选择角色");
        LambdaQueryWrapper<SysUserRole> deleteLqw = new LambdaQueryWrapper<>();
        deleteLqw.eq(SysUserRole::getRoleId, roleId);
        deleteLqw.in(SysUserRole::getUserId, userIds);
        if (userRoleMapper.delete(deleteLqw) != userIds.size()) {
            throw new BizException("删除失败, 请联系管理员");
        }
    }

    @Override
    public List<SysRole> listRoleByIds(Set<Long> roleIds) {
        return roleManager.listByIds(roleIds);
    }
}
